/*****
 Code is from US Secure Hash Algorithm 1 RFC.
 
 D. Eastlake and P. Jones, "US Secure Hash Algorithm 1 (SHA1),"
 Internet Engineering Task Force RFC 3174, September 2001.
 *****/

#include <cstdlib>
#include <cstring>
#include <cstdio>

#include <string>

#include "sha1.h"

enum sha_result_enum {
  shaSuccess = 0,
  shaNull,            /* Null pointer parameter */
  shaInputTooLong,    /* input data too long */
  shaStateError       /* called Input after Result */
};


#define SHA1HashSize 20

/*
 *  This structure will hold context information for the SHA-1
 *  hashing operation
 */
struct SHA1Context
{
  unsigned __int32 Intermediate_Hash[SHA1HashSize/4]; /* Message Digest  */
  unsigned __int32 Length_Low;            /* Message length in bits      */
  unsigned __int32 Length_High;           /* Message length in bits      */                               
  unsigned __int16 Message_Block_Index; /* Index into message block array   */
  unsigned char    Message_Block[64];      /* 512-bit message blocks      */

  int Computed;               /* Is the digest computed?         */
  int Corrupted;             /* Is the message digest corrupted? */

  SHA1Context() { reset(); }

  void reset();
  void input(const unsigned char* buf, size_t buflen);
  void hashresult(unsigned char raw_digest[SHA1HashSize]); // raw is of SHA1HashSize

  void pad_message();
  void process_message_block();
};

/*
 *  sha1.c
 *
 *  Description:
 *      This file implements the Secure Hashing Algorithm 1 as
 *      defined in FIPS PUB 180-1 published April 17, 1995.
 *
 *      The SHA-1, produces a 160-bit message digest for a given
 *      data stream.  It should take about 2**n steps to find a
 *      message with the same digest as a given message and
 *      2**(n/2) to find any two messages with the same digest,
 *      when n is the digest size in bits.  Therefore, this
 *      algorithm can serve as a means of providing a
 *      "fingerprint" for a message.
 *
 *  Portability Issues:
 *      SHA-1 is defined in terms of 32-bit "words".  This code
 *      uses <stdint.h> (included via "sha1.h" to define 32 and 8
 *      bit unsigned integer types.  If your C compiler does not
 *      support 32 bit unsigned integers, this code is not
 *      appropriate.
 *
 *  Caveats:
 *      SHA-1 is designed to work with messages less than 2^64 bits
 *      long.  Although SHA-1 allows a message digest to be generated
 *      for messages of any number of bits less than 2^64, this
 *      implementation only works with messages with a length that is
 *      a multiple of the size of an 8-bit character.
 *
 */

/*
 *  Define the SHA1 circular left shift macro
 */
#define SHA1CircularShift(bits,word) \
                (((word) << (bits)) | ((word) >> (32-(bits))))

/*
 *  SHA1Reset
 *
 *  Description:
 *      This function will initialize the SHA1Context in preparation
 *      for computing a new SHA1 message digest.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to reset.
 *
 *  Returns:
 *      sha Error Code.
 *
 */

void
SHA1Context::reset()
{
  Length_Low             = 0;
  Length_High            = 0;
  Message_Block_Index    = 0;

  Intermediate_Hash[0]   = 0x67452301;
  Intermediate_Hash[1]   = 0xEFCDAB89;
  Intermediate_Hash[2]   = 0x98BADCFE;
  Intermediate_Hash[3]   = 0x10325476;
  Intermediate_Hash[4]   = 0xC3D2E1F0;

  Computed   = 0;
  Corrupted  = 0;
}

/*
 *  SHA1Result
 *
 *  Description:
 *      This function will return the 160-bit message digest into the
 *      Message_Digest array  provided by the caller.
 *      NOTE: The first octet of hash is stored in the 0th element,
 *            the last octet of hash in the 19th element.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to use to calculate the SHA-1 hash.
 *      Message_Digest: [out]
 *          Where the digest is returned.
 *
 *  Returns:
 *      sha Error Code.
 *
 */
void
SHA1Context::hashresult(unsigned char Message_Digest[SHA1HashSize])
{
  if (!Computed)
  {
    pad_message();
    memset(Message_Block, 0, 64); /* message may be sensitive, clear it out */
    Length_Low = 0;    /* and clear length */
    Length_High = 0;
    Computed = 1;
  }

  for(int i = 0; i < SHA1HashSize; ++i)
  {
    Message_Digest[i] = Intermediate_Hash[i>>2]
                        >> 8 * ( 3 - ( i & 0x03 ) );
  }
}

/*
 *  SHA1Input
 *
 *  Description:
 *      This function accepts an array of octets as the next portion
 *      of the message.
 *
 *  Parameters:
 *      context: [in/out]
 *          The SHA context to update
 *      message_array: [in]
 *          An array of characters representing the next portion of
 *          the message.
 *      length: [in]
 *          The length of the message in message_array
 *
 *  Returns:
 *      sha Error Code.
 *
 */
void
SHA1Context::input(const unsigned char* message_array,
                   size_t length)
{
  if (length == 0 || message_array == 0) return;

  if (Computed) reset(); // just reset the previous computation

  while(length-- && !Corrupted)
  {
    Message_Block[Message_Block_Index++] =
                    (*message_array & 0xFF);

    Length_Low += 8;
    if (Length_Low == 0)
    {
        Length_High++;
        if (Length_High == 0)
        {
            /* Message is too long */
            Corrupted = 1;
        }
    }

    if (Message_Block_Index == 64)
      process_message_block();

    message_array++;
  }
}

/*
 *  SHA1ProcessMessageBlock
 *
 *  Description:
 *      This function will process the next 512 bits of the message
 *      stored in the Message_Block array.
 *
 *  Parameters:
 *      None.
 *
 *  Returns:
 *      Nothing.
 *
 *  Comments:

 *      Many of the variable names in this code, especially the
 *      single character names, were used because those were the
 *      names used in the publication.
 *
 *
 */
void
SHA1Context::process_message_block()
{
  const unsigned __int32 K[] =    {       /* Constants defined in SHA-1   */
                          0x5A827999,
                          0x6ED9EBA1,
                          0x8F1BBCDC,
                          0xCA62C1D6
                          };
  int                   t = 0;                    /* Loop counter                */
  unsigned __int32      temp = 0;                 /* Temporary word value        */
  unsigned __int32      W[80];                    /* Word sequence               */
  unsigned __int32      A=0, B=0, C=0, D=0, E=0;  /* Word buffers                */

  /*
    *  Initialize the first 16 words in the array W
    */
  for(t = 0; t < 16; t++)
  {
      W[t]  = Message_Block[t * 4] << 24;
      W[t] |= Message_Block[t * 4 + 1] << 16;
      W[t] |= Message_Block[t * 4 + 2] << 8;
      W[t] |= Message_Block[t * 4 + 3];
  }

  for(t = 16; t < 80; t++)
  {
      W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
  }

  A = Intermediate_Hash[0];
  B = Intermediate_Hash[1];
  C = Intermediate_Hash[2];
  D = Intermediate_Hash[3];
  E = Intermediate_Hash[4];

  for(t = 0; t < 20; t++)
  {
      temp =  SHA1CircularShift(5,A) +
              ((B & C) | ((~B) & D)) + E + W[t] + K[0];
      E = D;
      D = C;
      C = SHA1CircularShift(30,B);

      B = A;
      A = temp;
  }

  for(t = 20; t < 40; t++)
  {
      temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
      E = D;
      D = C;
      C = SHA1CircularShift(30,B);
      B = A;
      A = temp;
  }

  for(t = 40; t < 60; t++)
  {
      temp = SHA1CircularShift(5,A) +
              ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
      E = D;
      D = C;
      C = SHA1CircularShift(30,B);
      B = A;
      A = temp;
  }

  for(t = 60; t < 80; t++)
  {
      temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
      E = D;
      D = C;
      C = SHA1CircularShift(30,B);
      B = A;
      A = temp;
  }

  Intermediate_Hash[0] += A;
  Intermediate_Hash[1] += B;
  Intermediate_Hash[2] += C;
  Intermediate_Hash[3] += D;
  Intermediate_Hash[4] += E;

  Message_Block_Index = 0;
}

/*
 *  SHA1PadMessage
 *

 *  Description:
 *      According to the standard, the message must be padded to an even
 *      512 bits.  The first padding bit must be a '1'.  The last 64
 *      bits represent the length of the original message.  All bits in
 *      between should be 0.  This function will pad the message
 *      according to those rules by filling the Message_Block array
 *      accordingly.  It will also call the ProcessMessageBlock function
 *      provided appropriately.  When it returns, it can be assumed that
 *      the message digest has been computed.
 *
 *  Parameters:
 *      context: [in/out]
 *          The context to pad
 *      ProcessMessageBlock: [in]
 *          The appropriate SHA*ProcessMessageBlock function
 *  Returns:
 *      Nothing.
 *
 */

void
SHA1Context::pad_message()
{
  /*
    *  Check to see if the current message block is too small to hold
    *  the initial padding bits and length.  If so, we will pad the
    *  block, process it, and then continue padding into a second
    *  block.
    */
  if (Message_Block_Index > 55)
  {
    Message_Block[Message_Block_Index++] = 0x80;
    while(Message_Block_Index < 64)
    {
      Message_Block[Message_Block_Index++] = 0;
    }

    process_message_block();

    while(Message_Block_Index < 56)
    {
      Message_Block[Message_Block_Index++] = 0;
    }
  }
  else
  {
      Message_Block[Message_Block_Index++] = 0x80;
      while(Message_Block_Index < 56)
      {

        Message_Block[Message_Block_Index++] = 0;
      }
  }

  /*
    *  Store the message length as the last 8 octets
    */
  Message_Block[56] = Length_High >> 24;
  Message_Block[57] = Length_High >> 16;
  Message_Block[58] = Length_High >> 8;
  Message_Block[59] = Length_High;
  Message_Block[60] = Length_Low >> 24;
  Message_Block[61] = Length_Low >> 16;
  Message_Block[62] = Length_Low >> 8;
  Message_Block[63] = Length_Low;

  process_message_block();
}

/////////////////////////sha1///////////////////////
const char*
socklib::sha1digest::digest(const void* instr, size_t instr_len)
{
  SHA1Context context;
  context.input((const unsigned char*)instr, instr_len);
  
  context.hashresult(digest_raw);
  char* hr = digest_hex_str;

  for(int i = 0; i < SHA1HashSize; i++) {
    unsigned char c = digest_raw[i];
    unsigned char ch = c >> 4;
    unsigned char cl = c & 0xF;

    if (0 <= ch && ch <= 9)
      *hr++ = '0' + ch;
    else
      *hr++ =  'A' + ch - 10;

    if (0 <= cl && cl <= 9)
      *hr++ = '0' + cl;
    else
      *hr++ = 'A' + cl - 10;
  }

  *hr = 0;

  return digest_hex_str;
}

std::string
socklib::sha1hex(const void* instr, size_t instr_len)
{
  sha1digest sha1(instr, instr_len);
  return std::string(sha1.digest_hex_str);
}

 